#! /usr/bin/perl

# This is the ESOS network rc script; it parses the network configuration
# file and configures general network settings and interfaces accordingly.
# Original INI parsing sample code came from here: http://stackoverflow.com/\
# questions/2014862/how-can-i-access-ini-files-from-perl

use strict;

my $ini_file = "/etc/network.conf";
my $hosts_file = "/etc/hosts";
my $extra_hosts_file = "/etc/xtra_hosts";
my $resolv_file = "/etc/resolv.conf";
my $auto_dhcp_file = "/var/state/esos/auto_dhcp";
my $default_hostname = "localhost";
my $dhcp_prog = "/sbin/udhcpc";
my $ifconfig_prog = "/sbin/ifconfig";
my $route_prog = "/sbin/route";
my $hostname_prog = "/bin/hostname";
my $ip_prog = "/sbin/ip";
my $ifenslave_prog = "/sbin/ifenslave";
my $brctl_prog = "/usr/sbin/brctl";
my $ethtool_prog = "/usr/sbin/ethtool";
my $vconfig_prog = "/sbin/vconfig";
my $net_if_dir = "/sys/class/net";
my $section;
my @sections;
my %ini_hash;
my $hostname;
my $domainname;
my $fqdn;

# Check arguments
if ((@ARGV != 1) || (($ARGV[0] ne "start") && ($ARGV[0] ne "stop") &&
    ($ARGV[0] ne "apply"))) {
    print("Usage: $0 {start | stop | apply}\n");
    exit(1);
}

sub rem_dflt_gw {
    my $default_gw = `netstat -rn | grep ^0.0.0.0 | \
        awk '{print \$2}' | tr '\n' ' '`;
    if ($default_gw ne "") {
        print("Deleting the current default route(s): $default_gw\n");
        system($ip_prog . " route flush 0/0 2>&1");
        if ($? == -1) {
            print("ERROR: Running $ip_prog failed: $!\n");
        } else {
            my $exit_status = $? >> 8;
            if ($exit_status != 0) {
                print("ERROR: The $ip_prog program exited with " .
                    "value $exit_status.\n");
            }
        }
    }
}

if (($ARGV[0] eq "start") || ($ARGV[0] eq "apply")) {
    # Parse the ESOS network configuration file
    open(INI, "$ini_file") || die("ERROR: Can't open $ini_file: $!");
    while (<INI>) {
        chomp;
        # Get the INI file section
        if (/^\s*\[(.+)\].*/) {
            $section = $1;
            push(@sections, $section);
            next;
        }
        # Get the INI file keyword/value pairs
        if (/^(.*)\s*=\s*(.*)$/) {
            (my $keyword, my $value) = split(/=/, $_, 2);
            $keyword =~ s/^\s+//;
            $keyword =~ s/\s+$//;
            $value =~ s/^\s+//;
            $value =~ s/\s+$//;
            # Put them into a hash
            $ini_hash{$section . ":" . $keyword} = $value;
        }
    }
    close(INI);

    # Get/check the host name and domain name values
    if (exists($ini_hash{"general:hostname"})) {
        $hostname = $ini_hash{"general:hostname"};
        if ($hostname eq "") {
            print("WARNING: The 'hostname' value is empty -- " .
                "using a default value.\n");
            $hostname = $default_hostname;
        }
    }
    if (exists($ini_hash{"general:domainname"})) {
        $domainname = $ini_hash{"general:domainname"};
    }
    if ($domainname eq "") {
        $fqdn = $hostname;
    } else {
        $fqdn = $hostname . "." . $domainname;
    }

    # Set the host name (and DNS domain name if provided)
    print("Setting system host name: $fqdn\n");
    system($hostname_prog . " " . $fqdn . " 2>&1");
    if ($? == -1) {
        print("ERROR: Running $hostname_prog failed: $!\n");
    } else {
        my $exit_status = $? >> 8;
        if ($exit_status != 0) {
            print("ERROR: The $hostname_prog program exited with " .
                "value $exit_status.\n");
        }
    }

    # Write the /etc/hosts file
    print("Updating hosts file...\n");
    open(HOSTS, "> $hosts_file") || die("ERROR: Can't open $hosts_file: $!");
    print(HOSTS "# This file is generated by rc.network; do not edit\n");
    print(HOSTS "# Use the '$extra_hosts_file' file for additional " .
        "host entries\n\n");
    if ($hostname eq $default_hostname) {
        print(HOSTS "127.0.0.1\t$hostname\n");
        print(HOSTS "::1\t\t$hostname\n");
    } else {
        print(HOSTS "127.0.0.1\tlocalhost $hostname $fqdn\n");
        print(HOSTS "::1\t\tlocalhost $hostname $fqdn\n");
    }

    # If the extra-hosts file exists, add them to our hosts file
    if ((-e $extra_hosts_file) && (-r $extra_hosts_file)) {
        print(HOSTS "\n");
        open(EXTRA_HOSTS, "< $extra_hosts_file") ||
            die("ERROR: Can't open $extra_hosts_file: $!");
        while (my $line = <EXTRA_HOSTS>) {
            print(HOSTS "$line");
        }
        close(EXTRA_HOSTS);
    }
    close(HOSTS);

    # Flush any default gateways (should get set below, assuming new)
    if ($ARGV[0] eq "apply") {
        rem_dflt_gw();
    }

    # Bring up all interfaces
    my %conf_ifs = map { $_ => 1 } @sections;
    my @net_ifaces = glob($net_if_dir . "/*");
    foreach my $each (@net_ifaces) {
        my $iface = (split(/\//, $each))[-1];
        if (($iface =~ /enp\d+s\d+/) || ($iface =~ /eno\d+/) ||
            ($iface =~ /eth\d+/) || ($iface =~ /ib\d+/) ||
            ($iface =~ /m\d+p\d+/) || ($iface =~ /s\d+p\d+/) ||
            ($iface =~ /ens\d+/) || ($iface =~ /bond\d+/) ||
            ($iface =~ /priv\d+/)) {
            if ($ARGV[0] eq "apply") {
                # For "apply" we assume all the interfaces are up
                if (!exists($conf_ifs{$iface})) {
                    print("Removing IP settings from $iface " .
                        "(unconfigured)...\n");
                    system($ifconfig_prog . " " . $iface . " 0.0.0.0 2>&1");
                    if ($? == -1) {
                        print("ERROR: Running $ifconfig_prog failed: $!\n");
                        next;
                    }
                    my $exit_status = $? >> 8;
                    if ($exit_status != 0) {
                        print("ERROR: The $ifconfig_prog program exited with " .
                            "value $exit_status.\n");
                        next;
                    }
                }
            } else {
                # This must be a "start" action
                print("Bringing up interface $iface...\n");
                system($ifconfig_prog . " " . $iface . " up 2>&1");
                if ($? == -1) {
                    print("ERROR: Running $ifconfig_prog failed: $!\n");
                    next;
                }
                my $exit_status = $? >> 8;
                if ($exit_status != 0) {
                    print("ERROR: The $ifconfig_prog program exited with " .
                        "value $exit_status.\n");
                    next;
                }
            }
        }
    }

    # Handle auto DHCP mode if state file set and no sections exist
    if (($ARGV[0] eq "start") && (-e "$auto_dhcp_file") &&
        (scalar(@sections) == 1)) {
        print("Auto DHCP mode enabled, waiting for link...\n");
        sleep(5);
        foreach my $each (@net_ifaces) {
            my $iface = (split(/\//, $each))[-1];
            if (($iface =~ /enp\d+s\d+/) || ($iface =~ /eno\d+/) ||
                ($iface =~ /eth\d+/) || ($iface =~ /ib\d+/) ||
                ($iface =~ /m\d+p\d+/) || ($iface =~ /s\d+p\d+/) ||
                ($iface =~ /ens\d+/) || ($iface =~ /bond\d+/) ||
                ($iface =~ /priv\d+/)) {
                my $iface_carrier_file = "$net_if_dir" . "/" .
                    "$iface" . "/carrier";
                open(IFACE_CARRIER, "$iface_carrier_file") ||
                    die("ERROR: Can't open $iface_carrier_file: $!");
                my @iface_carrier_lines = <IFACE_CARRIER>;
                close(IFACE_CARRIER);
                if ($iface_carrier_lines[0] =~ /1/) {
                    print("Interface $iface has link, running $dhcp_prog...\n");
                    system($dhcp_prog . " -i " . $iface . " -b " .
                        " -q -F " . $hostname . " 2>&1");
                    if ($? == -1) {
                        print("ERROR: Running $dhcp_prog failed: $!\n");
                        next;
                    }
                    my $exit_status = $? >> 8;
                    if ($exit_status != 0) {
                        print("ERROR: The $dhcp_prog program exited with " .
                            "value $exit_status.\n");
                        next;
                    }
                    # We only run DHCP on the first interface that has link
                    last;
                }
            }
        }
    }

    # Configure the network interfaces
    foreach my $each (@sections) {
        if ($ARGV[0] eq "start") {
            # For bonding interfaces
            if ($each =~ /bond\d+$/) {
                my $bm_file = "$net_if_dir" . "/bonding_masters";
                my $bond_iface_file = "$net_if_dir" . "/" . "$each";

                # If the bonding interface doesn't exist, create it
                if (! -e "$bond_iface_file") {
                    print("Creating bonding interface $each...\n");
                    open(BONDING_MASTERS, "> $bm_file") ||
                        die("ERROR: Can't open $bm_file: $!");
                    print(BONDING_MASTERS "+$each");
                    close(BONDING_MASTERS);
                }

                # Parse the bonding options
                if (!exists($ini_hash{$each . ":bondopts"})) {
                    print("ERROR: The 'bondopts' name is missing!\n");
                    next;
                }
                my $bondopts = $ini_hash{$each . ":bondopts"};
                if ($bondopts eq "") {
                    print("WARNING: The 'bondopts' value is empty!\n");
                } else {

                    # Bring down the bond interface before modifying settings
                    print("Bringing down bonding interface $each...\n");
                    system($ifconfig_prog . " " . $each . " 0.0.0.0 down 2>&1");
                    if ($? == -1) {
                        print("ERROR: Running $ifconfig_prog failed: $!\n");
                        next;
                    }
                    my $exit_status = $? >> 8;
                    if ($exit_status != 0) {
                        print("ERROR: The $ifconfig_prog program exited with " .
                            "value $exit_status.\n");
                        next;
                    }

                    # Loop through the bonding options and set each
                    my @bondopt_vals = split(/ /, $bondopts);
                    foreach my $each_bondopt (@bondopt_vals) {
                        # Split each option into a key/value pair
                        (my $bondopt_key, my $bondopt_val) =
                            split(/=/, $each_bondopt, 2);
                        # Read the arp_ip_target file
                        my $arp_ip_tgt_file = "$net_if_dir" . "/" .
                            "$each" . "/bonding/arp_ip_target";
                        open(ARP_IP_TGT, "$arp_ip_tgt_file") ||
                            die("ERROR: Can't open $arp_ip_tgt_file: $!");
                        my @arp_ip_tgt_lines = <ARP_IP_TGT>;
                        close(ARP_IP_TGT);
                        # For new style syntax; example:
                        # arp_ip_target=192.168.1.1,192.168.1.2
                        if ("$bondopt_key" eq "arp_ip_target" &&
                            substr($bondopt_val, 0, 1) ne "+") {
                            my @arp_ip_vals = split(/,/, $bondopt_val);
                            foreach my $each_arp_ip (@arp_ip_vals) {
                                if (! grep(/$each_arp_ip/, @arp_ip_tgt_lines)) {
                                    print("$each: $bondopt_key -> " .
                                        "+$each_arp_ip\n");
                                    open(ARP_IP_TGT, "> $arp_ip_tgt_file") ||
                                        die("ERROR: Can't open " .
                                            "$arp_ip_tgt_file: $!");
                                    print(ARP_IP_TGT "+$each_arp_ip");
                                    close(ARP_IP_TGT);
                                }
                            }
                        # For old style synax; example:
                        # arp_ip_target=+192.168.1.1 arp_ip_target=+192.168.1.2
                        } elsif ("$bondopt_key" eq "arp_ip_target") {
                            my $arp_ip_no_plus = substr($bondopt_val, 1);
                            if (! grep(/$arp_ip_no_plus/, @arp_ip_tgt_lines)) {
                                print("$each: $bondopt_key -> " .
                                    "+$arp_ip_no_plus\n");
                                open(ARP_IP_TGT, "> $arp_ip_tgt_file") ||
                                    die("ERROR: Can't open " .
                                        "$arp_ip_tgt_file: $!");
                                print(ARP_IP_TGT "+$arp_ip_no_plus");
                                close(ARP_IP_TGT);
                            }
                        # For any other key that is not "primary"
                        } elsif ("$bondopt_key" ne "primary") {
                            print("$each: $bondopt_key -> $bondopt_val\n");
                            my $bonding_sysfs_file = "$net_if_dir" . "/" .
                                "$each" . "/bonding/" . "$bondopt_key";
                            open(SYSFS_FILE, "> $bonding_sysfs_file") ||
                                die("ERROR: Can't open " .
                                    "$bonding_sysfs_file: $!");
                            print(SYSFS_FILE "$bondopt_val");
                            close(SYSFS_FILE);
                        }
                    }
                }

                # Bring up the bond interface
                print("Bringing up bonding interface $each...\n");
                system($ifconfig_prog . " " . $each . " up 2>&1");
                if ($? == -1) {
                    print("ERROR: Running $ifconfig_prog failed: $!\n");
                    next;
                }
                my $exit_status = $? >> 8;
                if ($exit_status != 0) {
                    print("ERROR: The $ifconfig_prog program exited with " .
                        "value $exit_status.\n");
                    next;
                }

                # Parse the bonding slaves
                if (!exists($ini_hash{$each . ":slaves"})) {
                    print("ERROR: The 'slaves' name is missing!\n");
                    next;
                }
                my $slaves = $ini_hash{$each . ":slaves"};
                if ($slaves eq "") {
                    print("WARNING: The 'slaves' value is empty!\n");
                }

                # Add the slaves to the master bonding interface
                my @slave_vals = split(/,/, $slaves);
                foreach my $each_slave (@slave_vals) {
                    print("Enslaving device $each_slave to " .
                        "interface $each...\n");
                    system($ifenslave_prog . " " . $each . " " .
                        $each_slave . " 2>&1");
                    if ($? == -1) {
                        print("ERROR: Running $ifenslave_prog failed: $!\n");
                        next;
                    }
                    my $exit_status = $? >> 8;
                    if ($exit_status != 0) {
                        print("ERROR: The $ifenslave_prog program exited " .
                            "with value $exit_status.\n");
                        next;
                    }
                }

                # Parse the bonding options (again)
                if (!exists($ini_hash{$each . ":bondopts"})) {
                    print("ERROR: The 'bondopts' name is missing!\n");
                    next;
                }
                my $bondopts = $ini_hash{$each . ":bondopts"};
                if ($bondopts eq "") {
                    print("WARNING: The 'bondopts' value is empty!\n");
                } else {
                    my @bondopt_vals = split(/ /, $bondopts);
                    foreach my $each_bondopt (@bondopt_vals) {
                        # Split each option into a key/value pair
                        (my $bondopt_key, my $bondopt_val) =
                            split(/=/, $each_bondopt, 2);
                        # This is the only post-enslavement
                        # parameter we care about
                        if ("$bondopt_key" eq "primary") {
                            print("$each: $bondopt_key -> $bondopt_val\n");
                            my $bonding_sysfs_file = "$net_if_dir" . "/" .
                                "$each" . "/bonding/" . "$bondopt_key";
                            open(SYSFS_FILE, "> $bonding_sysfs_file") ||
                                die("ERROR: Can't open " .
                                    "$bonding_sysfs_file: $!");
                            print(SYSFS_FILE "$bondopt_val");
                            close(SYSFS_FILE);
                        }
                    }
                }
            }

            # For bridge interfaces
            if ($each =~ /br\d+/) {
                if (!exists($ini_hash{$each . ":brmembers"})) {
                    print("ERROR: The 'brmembers' name is missing!\n");
                    next;
                }
                my $brmembers = $ini_hash{$each . ":brmembers"};
                if ($brmembers eq "") {
                    print("WARNING: The 'brmembers' value is empty!\n");
                }

                # Create the bridge interface
                print("Creating bridge interface $each...\n");
                system($brctl_prog . " addbr " . $each . " 2>&1");
                if ($? == -1) {
                    print("ERROR: Running $brctl_prog failed: $!\n");
                    next;
                }
                my $exit_status = $? >> 8;
                if ($exit_status != 0) {
                    print("ERROR: The $brctl_prog program exited with " .
                        "value $exit_status.\n");
                    next;
                }

                # Bring up the new bridge interface
                print("Bringing up bridge interface $each...\n");
                system($ifconfig_prog . " " . $each . " up 2>&1");
                if ($? == -1) {
                    print("ERROR: Running $ifconfig_prog failed: $!\n");
                    next;
                }
                my $exit_status = $? >> 8;
                if ($exit_status != 0) {
                    print("ERROR: The $ifconfig_prog program exited with " .
                        "value $exit_status.\n");
                    next;
                }

                # Add the member interfaces to the bridge
                my @brmember_vals = split(/,/, $brmembers);
                foreach my $each_brmember (@brmember_vals) {
                    print("Adding member interface $each_brmember to " .
                        "bridge $each...\n");
                    system($brctl_prog . " addif " . $each . " " .
                        $each_brmember . " 2>&1");
                    if ($? == -1) {
                        print("ERROR: Running $brctl_prog failed: $!\n");
                        next;
                    }
                    my $exit_status = $? >> 8;
                    if ($exit_status != 0) {
                        print("ERROR: The $brctl_prog program exited with " .
                            "value $exit_status.\n");
                        next;
                    }
                }
            }
        }

        # Interface configuration
        if (($each =~ /enp\d+s\d+/) || ($each =~ /eno\d+/) ||
            ($each =~ /eth\d+/) || ($each =~ /ib\d+/) ||
            ($each =~ /m\d+p\d+/) || ($each =~ /s\d+p\d+/) ||
            ($each =~ /bond\d+/) || ($each =~ /br\d+/) ||
            ($each =~ /ens\d+/) || ($each =~ /priv\d+/)) {
            # If its a VLAN interface, make sure it exists
            if ($each =~ /.*\.\d+/) {
                my $vlan_iface = $net_if_dir . "/" . $each;
                if (! -e "$vlan_iface") {
                    printf("Creating VLAN interface " . $each . "...\n");
                    my @vlan_if_parts = split(/\./, $each);
                    system($vconfig_prog . " add " . $vlan_if_parts[0] .
                        " " . $vlan_if_parts[1] . " 2>&1");
                    if ($? == -1) {
                        print("ERROR: Running $vconfig_prog failed: $!\n");
                        next;
                    }
                    my $exit_status = $? >> 8;
                    if ($exit_status != 0) {
                        print("ERROR: The $vconfig_prog program exited with " .
                            "value $exit_status.\n");
                        next;
                    }
                }
            }
            # Check the boot protocol (DHCP/static)
            my $hash_key = $each . ":bootproto";
            if (exists($ini_hash{$hash_key})) {
                if ((lc($ini_hash{$hash_key}) eq "dhcp") ||
                    (lc($ini_hash{$hash_key}) eq "static")) {
                    # The ethtool options are completely optional
                    if (exists($ini_hash{$each . ":ethtoolopts"})) {
                        my $ethtoolopts = $ini_hash{$each . ":ethtoolopts"};
                        if ($ethtoolopts ne "") {
                            # Run ethtool with options
                            print("Applying ethtool options to " .
                                "interface $each...\n");
                            system($ethtool_prog . " -s " . $each . " " .
                                $ethtoolopts . " 2>&1");
                            if ($? == -1) {
                                print("ERROR: Running $ethtool_prog " .
                                    "failed: $!\n");
                                next;
                            }
                            my $exit_status = $? >> 8;
                            if ($exit_status != 0) {
                                print("ERROR: The $ethtool_prog program " .
                                    "exited with value $exit_status.\n");
                                next;
                            }
                        }
                    }
                }
                if (lc($ini_hash{$hash_key}) eq "dhcp") {
                    if (!exists($ini_hash{$each . ":mtu"})) {
                        print("ERROR: The 'mtu' name is missing!\n");
                        next;
                    }
                    my $mtu = $ini_hash{$each . ":mtu"};
                    if ($mtu eq "") {
                        print("ERROR: The 'mtu' value is empty!\n");
                        next;
                    }
                    # Set MTU for interface
                    if ($each =~ /.*\.\d+/) {
                        # It's a VLAN interface so set the parent MTU first
                        my @vlan_if_parts = split(/\./, $each);
                        print("Setting MTU for interface " .
                            "$vlan_if_parts[0]...\n");
                        system($ip_prog . " link set " . $vlan_if_parts[0] .
                            " mtu " . $mtu . " 2>&1");
                        if ($? == -1) {
                            print("ERROR: Running $ip_prog failed: $!\n");
                            next;
                        }
                        my $exit_status = $? >> 8;
                        if ($exit_status != 0) {
                            print("ERROR: The $ip_prog program exited with " .
                                "value $exit_status.\n");
                            next;
                        }
                    }
                    print("Setting MTU for interface $each...\n");
                    system($ip_prog . " link set " . $each . " mtu " .
                        $mtu . " 2>&1");
                    if ($? == -1) {
                        print("ERROR: Running $ip_prog failed: $!\n");
                        next;
                    }
                    my $exit_status = $? >> 8;
                    if ($exit_status != 0) {
                        print("ERROR: The $ip_prog program exited with " .
                            "value $exit_status.\n");
                        next;
                    }
                    # Do DHCP configuration for interface
                    print("Running $dhcp_prog for interface $each...\n");
                    system($dhcp_prog . " -i " . $each . " -b " .
                        " -q -F " . $hostname . " 2>&1");
                    if ($? == -1) {
                        print("ERROR: Running $dhcp_prog failed: $!\n");
                        next;
                    }
                    my $exit_status = $? >> 8;
                    if ($exit_status != 0) {
                        print("ERROR: The $dhcp_prog program exited with " .
                            "value $exit_status.\n");
                        next;
                    }

                } elsif (lc($ini_hash{$hash_key}) eq "static") {
                    # Do static configuration for interface
                    print("Setting static IP configuration for " .
                        "interface $each...\n");
                    if (!exists($ini_hash{$each . ":ipaddr"})) {
                        print("ERROR: The 'ipaddr' name is missing!\n");
                        next;
                    }
                    if (!exists($ini_hash{$each . ":netmask"})) {
                        print("ERROR: The 'netmask' name is missing!\n");
                        next;
                    }
                    if (!exists($ini_hash{$each . ":broadcast"})) {
                        print("ERROR: The 'broadcast' name is missing!\n");
                        next;
                    }
                    if (!exists($ini_hash{$each . ":mtu"})) {
                        print("ERROR: The 'mtu' name is missing!\n");
                        next;
                    }
                    my $ipaddr = $ini_hash{$each . ":ipaddr"};
                    if ($ipaddr eq "") {
                        print("ERROR: The 'ipaddr' value is empty!\n");
                        next;
                    }
                    my $netmask = $ini_hash{$each . ":netmask"};
                    if ($netmask eq "") {
                        print("ERROR: The 'netmask' value is empty!\n");
                        next;
                    }
                    my $broadcast = $ini_hash{$each . ":broadcast"};
                    if ($broadcast eq "") {
                        print("ERROR: The 'broadcast' value is empty!\n");
                        next;
                    }
                    my $mtu = $ini_hash{$each . ":mtu"};
                    if ($mtu eq "") {
                        print("ERROR: The 'mtu' value is empty!\n");
                        next;
                    }
                    if ($each =~ /.*\.\d+/) {
                        # It's a VLAN interface so set the parent MTU first
                        my @vlan_if_parts = split(/\./, $each);
                        print("Setting MTU for interface " .
                            "$vlan_if_parts[0]...\n");
                        system($ip_prog . " link set " . $vlan_if_parts[0] .
                            " mtu " . $mtu . " 2>&1");
                        if ($? == -1) {
                            print("ERROR: Running $ip_prog failed: $!\n");
                            next;
                        }
                        my $exit_status = $? >> 8;
                        if ($exit_status != 0) {
                            print("ERROR: The $ip_prog program exited with " .
                                "value $exit_status.\n");
                            next;
                        }
                    }
                    system($ifconfig_prog . " " . $each . " " . $ipaddr .
                        " netmask " . $netmask . " broadcast " . $broadcast .
                        " mtu " . $mtu . " 2>&1");
                    if ($? == -1) {
                        print("ERROR: Running $ifconfig_prog failed: $!\n");
                        next;
                    }
                    my $exit_status = $? >> 8;
                    if ($exit_status != 0) {
                        print("ERROR: The $ifconfig_prog program exited with " .
                            "value $exit_status.\n");
                        next;
                    }

                } else {
                    print("ERROR: Unknown 'bootproto' value " .
                        "($ini_hash{$hash_key}) for interface $each!\n");
                }
            } else {
                print("ERROR: The 'bootproto' name is missing for " .
                    "interface $each!\n");
            }
        }
    }

    # Write the /etc/resolv.conf file if any name servers are set
    if (exists($ini_hash{"general:nameserver1"}) ||
        exists($ini_hash{"general:nameserver2"}) ||
        exists($ini_hash{"general:nameserver3"})) {
        my $nameserver1 = "";
        if (exists($ini_hash{"general:nameserver1"})) {
            $nameserver1 = $ini_hash{"general:nameserver1"};
        }
        my $nameserver2 = "";
        if (exists($ini_hash{"general:nameserver2"})) {
            $nameserver2 = $ini_hash{"general:nameserver2"};
        }
        my $nameserver3 = "";
        if (exists($ini_hash{"general:nameserver3"})) {
            $nameserver3 = $ini_hash{"general:nameserver3"};
        }
        if ($nameserver1 ne "" || $nameserver2 ne "" || $nameserver3 ne "") {
            print("Updating resolver configuration file...\n");
            open(RESOLV, "> $resolv_file") ||
                die("ERROR: Can't open $resolv_file: $!");
            print(RESOLV "# This file is generated by rc.network; " .
                "do not edit\n\n");
            if ($domainname ne "") {
                print(RESOLV "search $domainname\n");
            }
            if ($nameserver1 ne "") {
                print(RESOLV "nameserver $nameserver1\n");
            }
            if ($nameserver2 ne "") {
                print(RESOLV "nameserver $nameserver2\n");
            }
            if ($nameserver3 ne "") {
                print(RESOLV "nameserver $nameserver3\n");
            }
            close(RESOLV);
        }
    }

    # Add default gateway route if value is set in the configuration file
    if (exists($ini_hash{"general:defaultgw"})) {
        my $defaultgw = $ini_hash{"general:defaultgw"};
        if ($defaultgw ne "") {
            system($route_prog . " add -net default gw " . $defaultgw .
                " netmask 0.0.0.0 2>&1");
            if ($? == -1) {
                print("ERROR: Running $route_prog failed: $!\n");
            } else {
                my $exit_status = $? >> 8;
                if ($exit_status != 0) {
                    print("ERROR: The $route_prog program exited with " .
                        "value $exit_status.\n");
                }
            }
        }
    }

} elsif ($ARGV[0] eq "stop") {
    # Remove the default route (if there is one set)
    rem_dflt_gw();

    # Unconfigure all interfaces
    my @net_ifaces = glob($net_if_dir . "/*");
    foreach my $each (@net_ifaces) {
        my $iface = (split(/\//, $each))[-1];

        # Remove slaves from bond interface
        if ($iface =~ /bond\d+$/) {
            my @raw_bond_slaves = glob($net_if_dir . "/" . $iface . "/slave_*");
            foreach my $raw_slave_iface (@raw_bond_slaves) {
                my $slave_iface = (split('slave_', $raw_slave_iface))[-1];
                print("Removing interface $slave_iface from $iface...\n");
                system($ifenslave_prog . " -d " . $iface . " " .
                    $slave_iface . " 2>&1");
                if ($? == -1) {
                    print("ERROR: Running $ifenslave_prog failed: $!\n");
                    next;
                }
                my $exit_status = $? >> 8;
                if ($exit_status != 0) {
                    print("ERROR: The $ifenslave_prog program exited with " .
                        "value $exit_status.\n");
                    next;
                }
            }
        }

        # Bring down interface
        if (($iface =~ /enp\d+s\d+/) || ($iface =~ /eno\d+/) ||
            ($iface =~ /eth\d+/) || ($iface =~ /ib\d+/) ||
            ($iface =~ /m\d+p\d+/) || ($iface =~ /s\d+p\d+/) ||
            ($iface =~ /bond\d+/) || ($iface =~ /br\d+/) ||
            ($iface =~ /ens\d+/) || ($iface =~ /priv\d+/)) {
            if ($iface =~ /.*\.\d+/) {
                # Skip VLAN interfaces
                next;
            }
            print("Bringing down interface $iface...\n");
            system($ifconfig_prog . " " . $iface . " 0.0.0.0 down 2>&1");
            if ($? == -1) {
                print("ERROR: Running $ifconfig_prog failed: $!\n");
                next;
            }
            my $exit_status = $? >> 8;
            if ($exit_status != 0) {
                print("ERROR: The $ifconfig_prog program exited with " .
                    "value $exit_status.\n");
                next;
            }
        }

        # Delete bonding interface
        if ($iface =~ /bond\d+/) {
            print("Deleting bonding interface $iface...\n");
            my $bm_file = "$net_if_dir" . "/bonding_masters";
            open(BONDING_MASTERS, "> $bm_file") ||
                die("ERROR: Can't open $bm_file: $!");
            print(BONDING_MASTERS "-$iface");
            close(BONDING_MASTERS);
        }

        # Delete bridge interface
        if ($iface =~ /br\d+/) {
            print("Deleting bridge interface $iface...\n");
            system($brctl_prog . " delbr " . $iface . " 2>&1");
            if ($? == -1) {
                print("ERROR: Running $brctl_prog failed: $!\n");
                next;
            }
            my $exit_status = $? >> 8;
            if ($exit_status != 0) {
                print("ERROR: The $brctl_prog program exited with " .
                    "value $exit_status.\n");
                next;
            }
        }
    }
}

